#include<iostream>
#include<vector>
#include<string>
#include<algorithm>
#include<set>
using namespace std;

struct TrieNode {
    string word;
    unordered_map<char,TrieNode *> children;
    TrieNode() {
        this->word = "";
    }   
};

void insertTrie(TrieNode * root,const string & word) {
    TrieNode * node = root;
    for (auto c : word){
        if (!node->children.count(c)) {
            node->children[c] = new TrieNode();
        }
        node = node->children[c];
    }
    node->word = word;
}

class Solution {
public:
    int dirs[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};

    bool dfs(vector<vector<char>>& board, int x, int y, TrieNode * root, set<string> & res) {
        char ch = board[x][y];        
        if (!root->children.count(ch)) {
            return false;
        }
        root = root->children[ch];
        if (root->word.size() > 0) {
            res.insert(root->word);
        }

        board[x][y] = '#';
        for (int i = 0; i < 4; ++i) {
            int nx = x + dirs[i][0];
            int ny = y + dirs[i][1];
            if (nx >= 0 && nx < board.size() && ny >= 0 && ny < board[0].size()) {
                if (board[nx][ny] != '#') {
                    dfs(board, nx, ny, root, res);
                }
            }
        }
        board[x][y] = ch;

        return true;      
    }

    vector<string> ans1(vector<vector<char>> & board, vector<string> & words) {
        TrieNode * root = new TrieNode();
        set<string> res;
        vector<string> ans;

        for (auto & word: words){
            insertTrie(root,word);
        }
        for (int i = 0; i < board.size(); ++i) {
            for (int j = 0; j < board[0].size(); ++j) {
                dfs(board, i, j, root, res);
            }
        }        
        for (auto & word: res) {
            ans.emplace_back(word);
        }
        return ans;        
    }

    public:
    void m_dfs(int x, int y, vector<vector<char>>& board, string& word, int s){
        if(x < 0 || x >= board.size() || y < 0 || y >= board[0].size()){
            return;
        }
        if(s == word.size()){
            flag = true;
        }
        if(board[x][y] != word[s]){
            return;
        }
        m_dfs(x+1, y, board, word, s+1);
        m_dfs(x-1, y, board, word, s+1);
        m_dfs(x, y+1, board, word, s+1);
        m_dfs(x, y-1, board, word, s+1);
    }

    vector<string> ans2(vector<vector<char>>& board, vector<string>& words){
        int len = words.size();
        vector<string> ret;
        for(auto& w: words){
            for(int i = 0; i != board.size(); i++){
                for(int j = 0; j != board[0].size(); j++){
                    if(board[i][j] == w[0]){
                        m_dfs(i, j, board, w, 0);
                    }
                }
            }
            if(flag){
                ret.push_back(w);
                flag = false;
            }
        }

        return ret;
    }
private:
    bool flag = false;
};


void test(){
    Solution s;
    vector<vector<char>> board = {
        {'o','a','a','n'},
        {'e','t','a','e'},
        {'i','h','k','r'},
        {'i','f','l','v'}
    };
    vector<string> words = {"oath","pea","eat","rain"};

    vector<string> ret = s.ans1(board, words);
    int len = ret.size();
    for(int i = 0; i != len; i++){
        cout << ret[i] << endl;
    }
}

int main(void){
    test();

    return 0;
}